// 第 6 步：实现 poll

// step 1:
// 在 tcp_server.h 中写好基本的 #pragma once，并包含 tcp_socket.h（间接依赖
// socket_utils、logging 等模块）。暂时只保留命名空间和空结构体声明
#pragma once

#include <functional>
#include <string>
#include <sys/epoll.h>
#include <vector>

#include "tcp_socket.h"
#include "logging.h"
#include "time_utils.h"

namespace Common {
// step 2:
// 服务器需要一个监听用的 TCPSocket、用于 epoll 的 fd、事件数组、收发队列、回调等。构造函数接收 Logger&，并让监听 socket 复用同一个 logger
struct TCPServer {
  explicit TCPServer(Logger &logger)
      : listener_socket_(logger), logger_(logger) {}

  // step 3:
  // 在头文件中补充三个公开方法：listen、poll、sendAndRecv，以及一个私有辅助方法 addToEpollList。每个函数简短注释说明职责
  auto listen(const std::string &iface, int port) -> void;
  auto poll() noexcept -> void;
  auto sendAndRecv() noexcept -> void;

private:
  auto addToEpollList(TCPSocket *socket);

public:
  int epoll_fd_ = -1;
  TCPSocket listener_socket_;
  epoll_event events_[1024];

  std::vector<TCPSocket *> receive_sockets_;
  std::vector<TCPSocket *> send_sockets_;

  std::function<void(TCPSocket *s, Nanos rx_time)> recv_callback_ = nullptr;
  std::function<void()> recv_finished_callback_ = nullptr;

  std::string time_str_;
  Logger &logger_;
};

// step 4:
// 切换到 tcp_server.cpp。首先包含头文件，并在 Common 命名空间内实现 addToEpollList：构造边缘触发、读事件的 epoll_event，把 TCPSocket* 放进 event.data.ptr，方便回调时取回
auto TCPServer::addToEpollList(TCPSocket *socket) {
  epoll_event ev{EPOLLET | EPOLLIN, {reinterpret_cast<void *>(socket)}};
  return !epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, socket->socket_fd_, &ev);
}

// step 5:
// 创建 epoll fd。
// 复用 TCPSocket::connect 在指定接口/端口上创建监听 socket。
// 然后把监听 fd 加入 epoll 监听列表。过程中用 ASSERT 校验，出现错误时触发 FATAL。
auto TCPServer::listen(const std::string &iface, int port) -> void {
  epoll_fd_ = epoll_create(1);
  ASSERT(epoll_fd_ >= 0, "epoll_create() failed error:" + std::string(std::strerror(errno)));

  ASSERT(listener_socket_.connect("", iface, port, true) >= 0,
         "Listener socket failed to connect. iface:" + iface + " port:" + std::to_string(port) + " error:" +
         std::string(std::strerror(errno)));

  ASSERT(addToEpollList(&listener_socket_), "epoll_ctl() failed. error:" + std::string(std::strerror(errno)));
}

// step 6:
// 调用 epoll_wait 非阻塞轮询事件。
// 针对 EPOLLIN、EPOLLOUT、EPOLLERR | EPOLLHUP 分别维护 receive_sockets_ 和 send_sockets_。
// 如果监听 socket 收到可读事件，说明有新连接，设置标志并在循环外统一 accept，直到耗尽（边缘触发的经典写法）。
// 新连接建立后，设置非阻塞、禁用 Nagle，记录日志，创建新的 TCPSocket 并加入 epoll
auto TCPServer::poll() noexcept -> void {
  const int max_events = 1 + send_sockets_.size() + receive_sockets_.size();

  const int n = epoll_wait(epoll_fd_, events_, max_events, 0);
  bool have_new_connection = false;
  for (int i = 0; i < n; ++i) {
    const auto &event = events_[i];
    auto socket = reinterpret_cast<TCPSocket *>(event.data.ptr);

    if (event.events & EPOLLIN) {
      if (socket == &listener_socket_) {
        logger_.log("%:% %() % EPOLLIN listener_socket:%\n", __FILE__, __LINE__, __FUNCTION__,
                    Common::getCurrentTimeStr(&time_str_), socket->socket_fd_);
        have_new_connection = true;
        continue;
      }
      logger_.log("%:% %() % EPOLLIN socket:%\n", __FILE__, __LINE__, __FUNCTION__,
                  Common::getCurrentTimeStr(&time_str_), socket->socket_fd_);
      if (std::find(receive_sockets_.begin(), receive_sockets_.end(), socket) == receive_sockets_.end())
        receive_sockets_.push_back(socket);
    }

    if (event.events & EPOLLOUT) {
      logger_.log("%:% %() % EPOLLOUT socket:%\n", __FILE__, __LINE__, __FUNCTION__,
                  Common::getCurrentTimeStr(&time_str_), socket->socket_fd_);
      if (std::find(send_sockets_.begin(), send_sockets_.end(), socket) == send_sockets_.end())
        send_sockets_.push_back(socket);
    }

    if (event.events & (EPOLLERR | EPOLLHUP)) {
      logger_.log("%:% %() % EPOLLERR socket:%\n", __FILE__, __LINE__, __FUNCTION__,
                  Common::getCurrentTimeStr(&time_str_), socket->socket_fd_);
      if (std::find(receive_sockets_.begin(), receive_sockets_.end(), socket) == receive_sockets_.end())
        receive_sockets_.push_back(socket);
    }
  }

  while (have_new_connection) {
    logger_.log("%:% %() % have_new_connection\n", __FILE__, __LINE__, __FUNCTION__,
                Common::getCurrentTimeStr(&time_str_));
    sockaddr_storage addr;
    socklen_t addr_len = sizeof(addr);
    int fd = accept(listener_socket_.socket_fd_, reinterpret_cast<sockaddr *>(&addr), &addr_len);
    if (fd == -1)
      break;

    ASSERT(setNonBlocking(fd) && disableNagle(fd),
           "Failed to set non-blocking or no-delay on socket:" + std::to_string(fd));

    logger_.log("%:% %() % accepted socket:%\n", __FILE__, __LINE__, __FUNCTION__,
                Common::getCurrentTimeStr(&time_str_), fd);

    auto socket = new TCPSocket(logger_);
    socket->socket_fd_ = fd;
    socket->recv_callback_ = recv_callback_;
    ASSERT(addToEpollList(socket), "Unable to add socket. error:" + std::string(std::strerror(errno)));

    if (std::find(receive_sockets_.begin(), receive_sockets_.end(), socket) == receive_sockets_.end())
      receive_sockets_.push_back(socket);
  }
}
}  // namespace Common